#include "hwboard.h"
#include "plc_abi.h"
#include <stm32_def.h>
#include <Wire.h>

TwoWire i2c(PB7, PB6);

#include "RS485.h"
#include <SPI.h>
#include <flashFs.h>

SPIClass spi1(PB15, PB14, PB13);
// FlashFs flash_fs;

#include "SPIFlash.h"

SPIFlash fspi(&spi1, PD10);
void *spi_lock = nullptr;
FlashFs flash_fs;
// HardwareSerial Serial3(PB11, PB10);
RS485Class serial2(PA2, PA3, PF9, -1);
RS485Class serial3(PB10, PB11, PF8, -1);
HardwareSerial serial4(PC11, PC10);
RS485Class serial5(PC12, PD2, PD0, -1);
kSerial k1(&Serial1);
kSerial k2(&serial2);
kSerial k3(&serial3);
kSerial k4(&serial4);
kSerial k5(&serial5);
kSerial *kSerial::Serials[] = {&k1, &k2, &k3, &k4, &k5};
const u8 kSerial::Serials_nrs = 5;

// spi_io_def spi = {PD10, PB13, PB15, PB14, 1, 1, 20};
#include <pt100_hx711.h>

pt100_hx711 hx1(PE14, PE15);
#define pin_beep PA8

#include <IWatchdog.h>
#include <rte_data.h>
#include <kSerial.h>
#include <plc_rte.h>
#include <logger_rte.h>
#include <plc_retain.h>
#include <retain_stm32.h>
#include <stepper_rte.h>

const uint8_t boardBase::xport[] = {X1, X2, X3, X4};
const uint8_t boardBase::yport[] = {Y1, Y2, Y3, Y4, Y5, Y6};
#define LED_RUN PG1

hwboard::hwboard() {
    SCB->VTOR = (uint32_t) &VECTOR_ISR;
    plc_var.info.toInit = 0;
    plc_var.info.reboot = 0;
}

void hwboard::beep(bool onoff) {
    if (onoff)
        digitalWrite(pin_beep, HIGH);
    else
        digitalWrite(pin_beep, LOW);
}

int hwboard::begin(u32 tick) {
    pinMode(pin_beep, OUTPUT);
    digitalWrite(pin_beep, HIGH);
    i2c.begin(MASTER_ADDRESS);
    rte.set_state(PLC_STATUS::Booting);
    spi_lock = rtos::Create_Mutex();
#ifdef USE_LFS
    fspi.init();
    flash_fs.begin(&fspi, PD10);
    flash_fs.dir();
#endif
    plc_var.info.rst_mode = IWatchdog.isReset();
    boardBase::begin(tick);

    pinMode(X1, INPUT_PULLUP);
    pinMode(X2, INPUT_PULLUP);
    pinMode(X3, INPUT_PULLUP);
    pinMode(X4, INPUT_PULLUP);
    pinMode(XA1, INPUT_ANALOG);
    // atemp_init = analogRead(ATEMP);
    analogReadResolution(12);
    hx1.begin();
    pinMode(Y1, OUTPUT);
    pinMode(Y2, OUTPUT);
    pinMode(Y3, OUTPUT);
    pinMode(Y4, OUTPUT);
    pinMode(Y5, OUTPUT);
    pinMode(Y6, OUTPUT);
    analogWriteResolution(12);
    pinMode(YA1, OUTPUT);
    pinMode(YA2, OUTPUT);
    IWatchdog.begin(10000 * 1000);
    pinMode(LED_RUN, OUTPUT);
    // pinMode(LED_ERR, OUTPUT);
    // digitalWrite(LED_ERR, LOW);
    digitalWrite(LED_RUN, HIGH);

    digitalWrite(pin_beep, LOW);
    plc_var.info.wdg_en = 1;
    plc_var.config.wdg_time = IWDG_TIMEOUT_MAX / 1000;
    plc_var.holdValue.ya_zero[0]=0;
    plc_var.holdValue.ya_zero[1]=0;
    return 0;
}

void Mcu_erase_blocks(u32 PageAddress, u8 NbPages) {
    FLASH_EraseInitTypeDef f;
    HAL_FLASH_Unlock();
    f.TypeErase = FLASH_TYPEERASE_PAGES;
    f.PageAddress = PageAddress;
    f.NbPages = NbPages;
    //设置PageError
    uint32_t PageError = 0;
    //调用擦除函数
    while (HAL_FLASHEx_Erase(&f, &PageError) != HAL_OK) {
        __NOP();
    }
}

void Mcu_Write_flash(u32 adr, const char *buf, int len) {
    for (int j = 0; j < len; j += 4) {
        uint32_t ret = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, adr + j, *(uint32_t *) (buf + j));
        switch (ret) {
            case HAL_OK:
                break;
            case HAL_TIMEOUT:
                logger.error("HAL_FLASH_Program() timeout!\n");
                break;
            case HAL_ERROR:
                logger.error("HAL_FLASH_Program() error 0x%08x, see *hal_flash.h for bit definitions\n",
                             HAL_FLASH_GetError());
                break;
            default:
                logger.error("HAL_FLASH_Program() returned unknown status %lu\n", ret);
        }
    }
}

#ifndef PLC

void app_flash_begin()
{
    FLASH_EraseInitTypeDef f;
    HAL_FLASH_Unlock();
    f.TypeErase = FLASH_TYPEERASE_PAGES;
    f.PageAddress = APP_START;
    f.NbPages = 128;
    //设置PageError
    uint32_t PageError = 0;
    //调用擦除函数
    while (HAL_FLASHEx_Erase(&f, &PageError) != HAL_OK)
    {
        __NOP();
    }
}

#endif

int hwboard::get_dbg_jmpr_get() {
    return 0;
}

int hwboard::get_rst_jmpr_get() {
    return 0;
}

extern "C" int GetRetainSize();

void hwboard::board_output(u32 tick) {
    digitalWrite(Y1, plcVar.digitalValue(IO_YO + 0) ? 0 : 1);
    digitalWrite(Y2, plcVar.digitalValue(IO_YO + 1) ? 0 : 1);
    digitalWrite(Y3, plcVar.digitalValue(IO_YO + 2) ? 0 : 1);
    digitalWrite(Y4, plcVar.digitalValue(IO_YO + 3) ? 0 : 1);
    digitalWrite(Y5, plcVar.digitalValue(IO_YO + 4) ? 0 : 1);
    // if (po6_freq > 0)
    // {
    //     po6_duty = 50;
    //     pwm_start(digitalPinToPinName(Y6), po6_freq, po6_duty, PERCENT_COMPARE_FORMAT);
    // }
    // else
    // {
    //     pwm_stop(digitalPinToPinName(Y6));
    digitalWrite(Y6, plcVar.digitalValue(IO_YO + 5) ? 0 : 1);
    // }
    analogWrite(YA1, 4095 * plc_var.analogValue32.ya[0] / 21.2 + plc_var.holdValue.ya_zero[0]);
    analogWrite(YA2, 4095 * plc_var.analogValue32.ya[1] / 21.2 + plc_var.holdValue.ya_zero[1]);
    if (plc_var.info.wdg_en) {
        if (!IWatchdog.isEnabled()) {
            IWatchdog.begin(plc_var.config.wdg_time * 1000);
        }
        IWatchdog.reload();
    }
}

#ifdef PLC
extern "C" int __init();
#endif

void hwboard::initReset() {
#ifdef PLC
    retain_self.reset();
    __init();
#endif
}

void hwboard::flashClear() {
#ifdef LFS
    plc_app_stop();
    flash_fs.clear();
    vTaskDelay(10);
    flash_fs.format();
    vTaskDelay(10);
    setReboot();
    while (1)
        ;
#endif
}

int hwboard::modbus_cb_read(u16 start_addr, u16 word_count, u16 *data_words) {
    if (boardBase::modbus_cb_read(start_addr, word_count, data_words) == 0)
        return 0;
    if (modbus_read_serial(start_addr, word_count, data_words, &k1, 61000, 130) == 0)
        return 0;
    if (modbus_read_serial(start_addr, word_count, data_words, &k2, 61300, 130) == 0)
        return 0;
    if (modbus_read_serial(start_addr, word_count, data_words, &k3, 61600, 130) == 0)
        return 0;
    if (modbus_read_serial(start_addr, word_count, data_words, &k4, 61900, 130) == 0)
        return 0;
    if (modbus_read_serial(start_addr, word_count, data_words, &k5, 62200, 130) == 0)
        return 0;

    for (int adr = start_addr; adr < start_addr + word_count; adr++) {
        switch (adr) {
            // case 60000:
            //     *data_words = ipcfg.ip1;
            //     break;
            // case 60001:
            //     *data_words = ipcfg.ip2;
            //     break;
            // case 60002:
            //     *data_words = ipcfg.ip3;
            //     break;
            // case 60003:
            //     *data_words = ipcfg.ip4;
            //     break;
            // case 60004:
            //     *data_words = ipcfg.mask1;
            //     break;
            // case 60005:
            //     *data_words = ipcfg.mask2;
            //     break;
            // case 60006:
            //     *data_words = ipcfg.mask3;
            //     break;
            // case 60007:
            //     *data_words = ipcfg.mask4;
            //     break;
            // case 60008:
            //     *data_words = ipcfg.gate1;
            //     break;
            // case 60009:
            //     *data_words = ipcfg.gate2;
            //     break;
            // case 60010:
            //     *data_words = ipcfg.gate3;
            //     break;
            // case 60011:
            //     *data_words = ipcfg.gate4;
            //     break;
            // case 60012:
            //     *data_words = ipSet;
            //     break;
            default:
                break;
        }
        data_words++;
    }
    return 0;
}

int hwboard::modbus_cb_write(u16 start_addr, u16 word_count, u16 *data_words) {
    if (boardBase::modbus_cb_write(start_addr, word_count, data_words) == 0)
        return 0;
    for (int adr = start_addr; adr < start_addr + word_count; adr++) {
        switch (adr) {
            // case 60000:
            //     ipcfg.ip1 = *data_words;
            //     break;
            // case 60001:
            //     ipcfg.ip2 = *data_words;
            //     break;
            // case 60002:
            //     ipcfg.ip3 = *data_words;
            //     break;
            // case 60003:
            //     ipcfg.ip4 = *data_words;
            //     break;
            // case 60004:
            //     ipcfg.mask1 = *data_words;
            //     break;
            // case 60005:
            //     ipcfg.mask2 = *data_words;
            //     break;
            // case 60006:
            //     ipcfg.mask3 = *data_words;
            //     break;
            // case 60007:
            //     ipcfg.mask4 = *data_words;
            //     break;
            // case 60008:
            //     ipcfg.gate1 = *data_words;
            //     break;
            // case 60009:
            //     ipcfg.gate2 = *data_words;
            //     break;
            // case 60010:
            //     ipcfg.gate3 = *data_words;
            //     break;
            // case 60011:
            //     ipcfg.gate4 = *data_words;
            //     break;
            // case 60012:
            //     ipSet = 1;
            //     break;
            default:
                break;
        }
        data_words++;
    }
    return word_count;
}

int clock_diag() {
    logger.debug("clock= %d \n", HAL_RCC_GetSysClockFreq());
    logger.debug("HCLK = %d \n", HAL_RCC_GetHCLKFreq());
    logger.debug("PCLK1 = %d\n", HAL_RCC_GetPCLK1Freq());
    logger.debug("PCLK2 = %d \n", HAL_RCC_GetPCLK2Freq());
    RCC_OscInitTypeDef RCC_OscInitStruct;
    HAL_RCC_GetOscConfig(&RCC_OscInitStruct);
    if (RCC_OscInitStruct.HSEState == RCC_HSE_ON) {
        logger.debug("HSE ON.\n");
    } else {
        logger.debug("HSE OFF.\n");
    }
    logger.debug("SysTick : CTRL=0x%x, LOAD= 0x%x, VAL= 0x%x , CALIB= 0x%x\n",
                 (unsigned int) SysTick->CTRL, (volatile unsigned int) SysTick->LOAD,
                 (volatile unsigned int) SysTick->VAL, (volatile unsigned int) SysTick->CALIB);
    return 0;
}

int hwboard::diag(u32 tick) {
    if (SCB->VTOR != (uint32_t) &VECTOR_ISR) {
        logger.error("VTOR:0x%x vs 0x%x\n", (u32) SCB->VTOR, &VECTOR_ISR);
    }
    clock_diag();
    for (int i = 0; i < 1; i++) {
        delay(100);
        logger.debug("Serial %d : \n", i);
        kSerial::get_serial(i)->diag();
        logger.debug("=======\n\n");
    }
    return 0;
}

void hwboard::plc_run_start() {
    if (digitalRead(LED_RUN) && (rtos::ticks() - last_led_tick) > 200) {
        digitalWrite(LED_RUN, LOW);
        last_led_tick = rtos::ticks();
    }
}

void hwboard::plc_run_end() {
    if (!digitalRead(LED_RUN) && (rtos::ticks() - last_led_tick) > 200) {
        digitalWrite(LED_RUN, HIGH);
        last_led_tick = rtos::ticks();
    }
}

int hwboard::load_app() {
    //
    return 0;
}

void hwboard::Poll(u32 tick) {
    hx1.loop();

    plc_var.analogValue32.xt[0] = hx1.value();

    plcVar.digitalValue(0, digitalRead(X1) ? 0 : 1);
    plcVar.digitalValue(1, digitalRead(X2) ? 0 : 1);
    plcVar.digitalValue(2, digitalRead(X3) ? 0 : 1);
    plcVar.digitalValue(3, digitalRead(X4) ? 0 : 1);
    plc_var.analogValue.xa[0] = (u16) analogRead(XA1);
    IWatchdog.reload();
}

__WEAK void write_bits_cb(uint16_t start_addr, uint16_t bit_count, uint8_t *packed_data) {
    //
}

__WEAK void write_outwords_cb(uint16_t start_addr, uint16_t word_count, uint16_t *data_words) {
    //
}

int rte_app::load() {
#ifdef RTE_APP
    plc_var.info.plc_curr_app = (plc_app_abi_t *)APP_START;
#else
    plc_var.info.plc_curr_app = (plc_app_abi_t *) &plc_app;
#endif
    return 0;
}

void hwboard::plc_run_stop() {
    digitalWrite(LED_RUN, LOW);
}

void hwboard::plc_run_error() {
    //    digitalWrite(LED_ERR, HIGH);
}

void hwboard::plc_run_empty() {
    digitalWrite(LED_RUN, HIGH);
}

mem_block *hwboard::get_retain() {
    return new retain_stm32(0x0807f000, 0x0807f800, 2048);
}

void hwboard::led_run(u8) {
}

void hwboard::led_err(u8) {
}

int hwboard::test_hw() {
    return 0;
}

kSerial *kSerial::get_serial(int fd) {
    if (fd < Serials_nrs) {
        return kSerial::Serials[fd];
    } else {
        logger.error("kSerial::get_serial: invalid fd %d\n", fd);
        return nullptr;
    }
}

USART_TypeDef *msg_uart = USART1;

kSerial *kSerial::get_mon_serial() {
    msg_uart = Serials[0]->serial->_serial.handle.Instance;
    return &k1;
}

kSerial *kSerial::get_firmata_serial() {
    return &k1;
}

stepper_t stepper;
struct fdb_default_kv_node default_kv_table[3] = {
        {"version", (void *) "2.0",   0, KV_STR_VALUE}, /* string KV */
        {"model",   (void *) HWMODEL, 0, KV_STR_VALUE}, /* string KV */
};

size_t default_kv_table_nrs = 2;